﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Krist_em_up.Components;
using Microsoft.Xna.Framework.Graphics;
using Krist_em_up.Characters.Enemies;
using Krist_em_up;
using Krist_em_up.Plateforms;
using Krist_em_up.Item;
using Microsoft.Xna.Framework.Audio;
using Krist_em_up.Music;
using Krist_em_up.Graphics;
using Krist_em_up.Interface.Gui;
using Krist_em_up.i18n;
using Krist_em_up.Item.Weapons;

namespace Krist_em_up.Characters
{
    public class Kris : Character
    {
        #region Animation & sprites properties
        private Animation runAnimation;
        private Animation jumpAnimation;
        private Animation idleAnimation;
        private AnimationPlayer sprite;
        private SpriteEffects effects;
        float secondPerFrame = 0.08f;
        #endregion

        public int Hp;        
        int lives = 3;
        int player;

        #region Graphics Related
        public const int HEIGHT = 62;
        public const int WIDTH = 88;
        int column = 0;
        int row = 1;
        Rectangle SourceRectangle;
        #endregion

        TimeSpan lastShot, reloadTimer;

        int timeSinceLastFrame;
        bool right = true;

        int varDamage;

        Bonus boost = null;

        public Bonus Bonus
        {
            get { return boost; }
        }

        public TimeSpan activation;
        float factor = 1;
        Texture2D bullet, guiBG;
        PlayerHealth GUIHealth;

        public ControlsConfig ctrls;

        private float movement;

        private bool isJumping, wasJumping;

        #region Die Vars
        private bool isDying;

        /// <summary>
        /// Timers to stop animation at its end 
        /// </summary>
        private TimeSpan startDiying, DieTimeAnimation;

        private Animation dieAnimation;
        #endregion

        private float jumpTime;
        public const int damage = 1;
        public const int MAX_HP = 5;
        public TimeSpan effectTime = new TimeSpan(0, 0, 0, 10, 0);

        protected Queue<Weapon> weapons;

        public int CurrentAmmo
        {
            get
            {
                return weapons.First().Ammunition;
            }
        }

        #region Physics Constants
        // Constants for controling horizontal movement
        private const float MoveAcceleration = 14000.0f;
        private const float MaxMoveSpeed = 2000.0f;
        public const float GroundDragFactor = 0.58f;
        private const float AirDragFactor = 0.65f;

        // Constants for controlling vertical movement
        private const float MaxJumpTime = 0.35f;
        private const float JumpLaunchspeed = -6000.0f;
        private const float GravityAcceleration = 3500.0f;
        private const float MaxFallSpeed = 600.0f;
        private const float JumpControlPower = 0.14f;
        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="game"></param>
        /// <param name="position"></param>
        /// <param name="ctrls"></param>
        public Kris(Kristemup game, Vector2 position, ControlsConfig ctrls,int player)
            : base(game, position)
        {
            textureAsset = "Characters/walking";
            SourceRectangle = new Rectangle(column * WIDTH, row * HEIGHT, WIDTH, HEIGHT);
            rectangle = new Rectangle((int)position.X, (int)position.Y, HEIGHT, WIDTH);
            reloadTimer = new TimeSpan(0, 0, 0, 0, 300);
            lastShot = -reloadTimer;
            prevPosition = position;
            timeSinceLastFrame = 0;
            GUIHealth = new PlayerHealth(game);

            // Init the sprites
            runAnimation = new Animation(game.Content.Load<Texture2D>("Characters/Krisbool"), secondPerFrame, WIDTH, true);
            jumpAnimation = new Animation(game.Content.Load<Texture2D>("Characters/KrisboolJump"), secondPerFrame, 81, false);
            idleAnimation = new Animation(game.Content.Load<Texture2D>("Characters/KrisboolIdle"), secondPerFrame, 81, true);
            dieAnimation = new Animation(game.Content.Load<Texture2D>("Characters/KrisDie"), 1f, 44, false);
            bullet = game.Content.Load<Texture2D>("bullet2");
            guiBG = game.Content.Load<Texture2D>("guiBG");

            DieTimeAnimation = new TimeSpan(0, 0, 0, 5, 0);
            sprite = new AnimationPlayer();
            sprite.PlayAnimation(idleAnimation);
            this.ctrls = ctrls;
            varDamage = damage;            
            Hp = MAX_HP;
            this.player = player;
            InitWeapons();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="move"></param>
        /// <param name="enemies"></param>
        public void Update(GameTime gameTime, bool move, LinkedList<Enemy> enemies)
        {
            if (!isDying)
            {
                reachEnd = false;
                timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
                GetInput(gameTime);
                ApplyPhysics(gameTime);
                //rectangle.Y -= sprite.Animation.FrameHeight - runAnimation.FrameHeight;
                if (isOnGround && !isJumping)
                {
                    if (speed.X == 0f)
                        sprite.PlayAnimation(idleAnimation);
                    else
                        sprite.PlayAnimation(runAnimation);
                }
                movement = 0.0f;
                isJumping = false;
                prevPosition = position;
                if (boost != null & game.time > activation + effectTime)
                {
                    boost.endEffect(this);
                    boost = null;
                }

                for (int i = 0; i < weapons.Count; i++)
                {
                    Weapon w = weapons.Dequeue();
                    w.Update(gameTime);
                    weapons.Enqueue(w);
                }
            }
            if (Hp <= 0)
            {
                if (boost != null)
                {
                    boost.endEffect(this);
                    boost = null;
                }
                Die(gameTime);
            }
            Vector2 end = game.map.GetEndPosition();
            if (rectangle.Intersects(new Rectangle((int)end.X * Plateform.Width, (int)end.Y * Plateform.Height, Plateform.Width, Plateform.Height)))
            {
                reachEnd = true;
            }
        }
        /// <summary>
        /// Update the bounding rectangle to match the sprite
        /// </summary>
        public override void UpdateRectangle()
        {
            rectangle = new Rectangle((int)position.X, (int)position.Y,
                sprite.Animation.FrameWidth, sprite.Animation.FrameHeight);
        }


        public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Vector2 origin)
        {
            //SpriteFont font = game.Content.Load<SpriteFont>("font");
            //spriteBatch.DrawString(font, position.Y.ToString(), new Vector2(0, 50), Color.White);
            spriteBatch.Begin();
            sprite.Draw(gameTime, spriteBatch, position - origin, effects);
            spriteBatch.End();
        }

        #region do not open, it stinks
        /// <summary>
        /// CACA fonction
        /// </summary>
        /// <param name="spritebatch"></param>
        //public void DrawGUI(SpriteBatch spritebatch)
        //{
        //    SpriteFont font = game.Content.Load<SpriteFont>("Fonts/Gamefont2");
        //    font.Spacing = -10;
        //    spritebatch.Begin();
        //    spritebatch.Draw(guiBG, new Rectangle(0, 0, game.game.GraphicsDevice.Viewport.Width, game.game.GraphicsDevice.Viewport.Height), Color.White);
        //    spritebatch.DrawString(font, "LEVEL 1", Vector2.Zero, Color.Red);
        //    spritebatch.Draw(bullet, new Vector2(10, font.MeasureString("L").Y + bullet.Width + 30), null, Color.White, (float)(-Math.PI / 2), Vector2.Zero, 1.5f,
        //        SpriteEffects.None, 0);
        //    switch (type)
        //    {
        //        case ammotype.rapid:
        //            munition = ammoR.ToString();
        //            break;
        //        case ammotype.scatter:
        //            munition = ammoS.ToString();
        //            break;
        //        default:
        //            munition = /*"∞"*/"~";
        //            break;
        //    }
        //    spritebatch.DrawString(font, "  X " + munition, new Vector2(10 + bullet.Height, font.MeasureString("L").Y + 25), Color.White);

        //    spritebatch.DrawString(font, Strings.vies + " : " + lives.ToString(), new Vector2(game.game.GraphicsDevice.Viewport.Width
        //        - font.MeasureString(Strings.vies + " : " + lives.ToString()).X, font.MeasureString("L").Y + 25), Color.Black);
        //    spritebatch.End();

        //    GUIHealth.Draw(spritebatch, (float)Hp / (float)MAX_HP);
        //    if (boost != null)
        //    {
        //        boost.DrawGui(spritebatch, new Vector2(0, font.MeasureString("L").Y * 1.5f + bullet.Width));
        //        spritebatch.Begin();
        //        spritebatch.DrawString(font, (effectTime - game.time + activation).Seconds.ToString() + "."
        //            + ((effectTime - game.time + activation).Milliseconds / 100).ToString(),
        //            new Vector2(0, font.MeasureString("L").Y + bullet.Width * 1.5f + boost.getHeight), Color.Black);
        //        spritebatch.End();

        //    }
        //}

        /// <summary>
        /// Soon will disapear
        /// </summary>
        /// <param name="gameTime"></param>
        //public void Shoot(TimeSpan gameTime)
        //{
        //    if (lastShot + reloadTimer < gameTime && canShoot)
        //    {
        //        if (type == ammotype.rapid)
        //        {
        //            ammoR--;
        //            if (ammoR <= 0)
        //                canShoot = false;
        //        }
        //        else if (type == ammotype.scatter)
        //        {
        //            ammoS--;
        //            if (ammoS <= 0)
        //                canShoot = false;
        //        }
        //        if (right)
        //        {
        //            Projectile balle = new Projectile(this, game, new Vector2(position.X + WIDTH, position.Y + HEIGHT / 2), new Vector2(1, 0));
        //            game.bullets.Add(balle);
        //            if (type == ammotype.scatter)
        //            {
        //                balle = new Projectile(this, game, new Vector2(position.X + WIDTH, bullet.Height + position.Y + HEIGHT / 2), new Vector2(1, 1));
        //                game.bullets.Add(balle);
        //                balle = new Projectile(this, game, new Vector2(position.X + WIDTH, -bullet.Height + position.Y + HEIGHT / 2), new Vector2(1, -1));
        //                game.bullets.Add(balle);
        //            }
        //        }
        //        else
        //        {
        //            Projectile balle = new Projectile(this, game, new Vector2(position.X, position.Y + HEIGHT / 2), new Vector2(-1, 0));
        //            game.bullets.Add(balle);
        //            if (type == ammotype.scatter)
        //            {
        //                balle = new Projectile(owner, game, new Vector2(position.X, bullet.Height + position.Y + HEIGHT / 2), new Vector2(-1, 1));
        //                game.bullets.Add(balle);
        //                balle = new Projectile(owner, game, new Vector2(position.X, -bullet.Height + position.Y + HEIGHT / 2), new Vector2(-1, -1));
        //                game.bullets.Add(balle);
        //            }
        //        }
        //        Sounds.PlayShoot();
        //        lastShot = gameTime;
        //    }
        //}

        #endregion

        public Vector2 getPosition()
        {
            return position;
        }


        /// <summary>
        /// Gets player horizontal movement and jump commands from input.
        /// </summary>
        private void GetInput(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            Clavier clavier = game.getClavier();
            if (keyboardState.IsKeyDown(ctrls.KeyLeft))
            {
                movement = -1.0f;
                effects = SpriteEffects.FlipHorizontally;
                right = false;
            }
            else if (keyboardState.IsKeyDown(ctrls.KeyRight))
            {
                movement = 1.0f;
                effects = SpriteEffects.None;
                right = true;
            }
            if (keyboardState.IsKeyDown(ctrls.KeyShoot))
                //Shoot(gameTime.TotalRealTime);
                weapons.First().Shoot(position, right);

            // Check if the player wants to jump.
            if (clavier.HasBeenPressed(ctrls.KeyChange))
            {
                //Met la 1ere arme à la fin
                weapons.Enqueue(weapons.Dequeue());
            }
            isJumping = keyboardState.IsKeyDown(ctrls.KeyJump);
        }

        /// <summary>
        /// Updates the player's speed and position based on input, gravity, etc.
        /// </summary>
        public void ApplyPhysics(GameTime gameTime)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            Vector2 previousPosition = position;

            // Base speed is a combination of horizontal movement control and
            // acceleration downward due to gravity.
            speed.X += movement * MoveAcceleration * elapsed;
            speed.Y = MathHelper.Clamp(speed.Y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed);

            speed.Y = DoJump(speed.Y, gameTime);

            // Apply pseudo-drag horizontally.
            if (isOnGround)
                speed.X *= GroundDragFactor * factor;
            else
                speed.X *= AirDragFactor * factor;
            // Prevent the player from running faster than his top speed.            
            speed.X = MathHelper.Clamp(speed.X, -MaxMoveSpeed, MaxMoveSpeed);

            // Apply speed.
            position += speed * elapsed;
            position = new Vector2((float)Math.Round(position.X), (float)Math.Round(position.Y));

            // If the player is now colliding with the level, separate them.
            UpdateRectangle();
            HandleCollisions();
            UpdateRectangle();
            // If the collision stopped us from moving, reset the speed to zero.
            if (position.X == previousPosition.X)
                speed.X = 0;

            if (position.Y == previousPosition.Y && isOnGround)
                speed.Y = 0;

        }

        private float DoJump(float speedY, GameTime gameTime)
        {
            // If the player wants to jump
            if (isJumping)
            {
                // Begin or continue a jump
                if ((!wasJumping && isOnGround) || jumpTime > 0.0f)
                {
                    if ((!wasJumping && isOnGround))
                        Sounds.PlayJump();
                    jumpTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
                    sprite.PlayAnimation(jumpAnimation);
                }

                // If we are in the ascent of the jump
                if (0.0f < jumpTime && jumpTime <= MaxJumpTime)
                {
                    // Fully override the vertical speed with a power curve that gives players more control over the top of the jump
                    speedY = JumpLaunchspeed * (1.0f - (float)Math.Pow(jumpTime / MaxJumpTime, JumpControlPower));
                }
                else
                {
                    // Reached the top of the jump
                    jumpTime = 0.0f;
                }
            }
            else
            {
                // Continues not jumping or cancels a jump in progress
                jumpTime = 0.0f;
            }
            wasJumping = isJumping;

            return speedY;
        }
        public void bonus(Bonus blabla, GameTime gametime)
        {
            if (boost != null)
                boost.endEffect(this);
            boost = blabla;
            boost.applyEffect(this);
            activation = game.time;
        }
        public int Damage
        {
            get { return varDamage; }
            set { varDamage = value; }
        }
        public float Factor
        {
            get { return factor; }
            set { factor = value; }
        }
        public int Lives
        {
            get { return lives; }
            set { lives = value; }
        }

        public void Die(GameTime gameTime)
        {
            if (!isDying)
            {
                sprite.PlayAnimation(dieAnimation);
                startDiying = gameTime.TotalGameTime;
                isDying = true;
            }
            else if (gameTime.TotalGameTime - startDiying >= DieTimeAnimation)
            {
                if (lives > 1)
                {
                    sprite.PlayAnimation(idleAnimation);
                    isDying = false;
                    lives--;
                    Hp = MAX_HP;
                }
                else
                {
                    lives = 0;
                    isDying = true;
                }
            }
        }

        public bool Damaged(int amount)
        {
            if (Hp>0)
            {
                Hp-= amount;
                if (Hp < 0)
                    Hp = 0;
                return true;
            }
            else
                return false;
        }

        protected void InitWeapons()
        {
            weapons = new Queue<Weapon>();
            weapons.Enqueue(new Gun(player, game));
            weapons.Enqueue(new AutomaticGun(player, game));
            weapons.Enqueue(new ScatterGun(player, game));
            weapons.Enqueue(new GrenadeLauncher(player, game));

        }
    }
}